package Measurements;

import java.io.FileWriter;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Vector;

import Basics.*;
import Derivation.TopDown;
import Parsing.*;
import Pattern.*;
import au.com.bytecode.opencsv.CSVWriter;

public class OnlineMeasurements 
{
	TopDown td;
	
	IntersectWithProgram iwp;
	
	public OnlineMeasurements () {}

	
	
	/*************************************************************************************************************/
	/** Title: MeasureAndWriteCSV																	
	/** Description: Apply's the function of the class and writes the result in CSV file 						
	/*************************************************************************************************************/
	
	public void MeasureAndWriteCSV (String path) throws IOException
	{
		DB.getInstance().DropTableIfExists("imports");
		DB.getInstance().DropTableIfExists("exports");
		DB.getInstance().DropTableIfExists("dealsWith");
		
		FileWriter fw = new FileWriter(path);
		CSVWriter writer = new CSVWriter(fw);
		ArrayList<String[]> data = new ArrayList<String[]>();
		data.add(new String[] {"Size of initial DB", "Size of original program", "Size of pattern", "Number of rules added in intersection", 
				"Duration for intersection in seconds", "Size of prov. After intersection", "Size of DB"});
		
		writer.writeAll(data);
		writer.flush();
		DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
		for (int i = 100; i < 600; i+=100) 
		{
			DB.getInstance().DropTableIfExists("imports");
			DB.getInstance().DropTableIfExists("exports");
			DB.getInstance().DropTableIfExists("dealsWith");
			ParseDB rfDB = new ParseDB ("yago2core_facts.clean.notypes.tsv", i);//C:\\Users\\amirgilad\\Downloads\\
			
			String[] array1 = this.MeasureIntersectionWithOriginalProgram();
			String[] array2 = this.MeasureTopDown();
			String[] array1and2 = new String[array1.length + array2.length + 1];
			System.arraycopy(array1, 0, array1and2, 1, array1.length);
			System.arraycopy(array2, 0, array1and2, array1.length + 1, array2.length);
			array1and2[0] = Integer.toString(i);
			writer.writeNext(array1and2);
			writer.flush();
			System.out.println("---------------------------------------------------------------------------------------------------------------------------------");
			System.out.println("Last Iteration Finished at: " + dateFormat.format(Calendar.getInstance().getTime()));
			System.out.println(String.format("Size of initial DB: %d\nSize of original program: %s\nSize of pattern: %s\nNumber of rules added in intersection: %s\n" 
				+ "Duration for intersection in seconds: %s\nSize of prov. After intersection: %s\nSize of DB: %s", i, array1[0], array1[1], array1[2], array1[3], array2[0], array2[1]));
			System.out.println("---------------------------------------------------------------------------------------------------------------------------------");
		}
		
	    writer.close();
	}
	
	
	/*************************************************************************************************************/
	/** Title: MeasureIntersectionWithOriginalProgram																	
	/** Description: Measures the time and size of added rules due to Intersection With the Original Program						
	/*************************************************************************************************************/
	
	public String[] MeasureIntersectionWithOriginalProgram ()
	{
		Pattern pattern = MakePattern();
		Program p = MakeProgram ();
		iwp = new IntersectWithProgram (p, pattern);
		int sizeOfOrigProgram = iwp.getP().getRules().size();
		long startTime = System.currentTimeMillis();
		iwp.getPattern().Renaming();
		iwp.IntersectNoTransitives();
		long endTime = System.currentTimeMillis();
		
		int sizeDiffProgram = iwp.getP().getRules().size() - sizeOfOrigProgram;
		double intersectionTime = (endTime - startTime)/1000;
		
		return new String[] {Integer.toString(p.getRules().size()), Integer.toString(iwp.getPattern().getSize()), Integer.toString(sizeDiffProgram), 
				Double.toString(intersectionTime)};
	}
	
	
	
	/*************************************************************************************************************/
	/** Title: MeasureTopDown																	
	/** Description:  Measures the time and size of selective Prov. via topDown						
	/*************************************************************************************************************/
	
	public String[] MeasureTopDown ()
	{
		long startTime = System.currentTimeMillis();
		this.td = new TopDown(iwp.getP());
		iwp.getPattern().getPatternVec().firstElement().get(0).setName(iwp.getPattern().getPatternVec().firstElement().get(0).getNewName());
		Atom root = new Atom (iwp.getPattern().getPatternVec().firstElement().get(0));
		td.Run(root);									//top-down on root of intersection
		long endTime = System.currentTimeMillis();
		double durationFullProv = (endTime - startTime)/1000;
		
		long numOfAtoms = td.getParser().getProvenance().values().size();
		
		return new String[] {Double.toString(durationFullProv), Long.toString(numOfAtoms), };
	}
	
	
	
	/*************************************************************************************************************/
	/** Title: MakePattern																	
	/** Description: Builds the pattern   						
	/*************************************************************************************************************/
	
	public Pattern MakePattern ()
	{
		////////////////////////////////////////////////
		Constant Canada = new Constant("Canada", "Country");
		Constant Andorra = new Constant("Andorra", "Country");
		PatternNode root = new PatternNode ("dealsWith",false, Canada, Andorra);
		PatternNode child1 = new PatternNode ("dealsWith",false, Andorra, Canada);
		root.setChildren(child1);
		child1.setParent(root); 

		Vector<PatternNode> rootVec = new Vector<PatternNode> ();
		rootVec.add(root);

		Vector<PatternNode> childVec = new Vector<PatternNode> ();
		childVec.add(child1);

		Vector<Vector<PatternNode>> pattern = new Vector<Vector<PatternNode>> ();
		pattern.add(rootVec); 
		pattern.add(childVec); 
		////////////////////////////////////////////////
		
		return new Pattern(pattern);
	}
	
	
	
	
	/*************************************************************************************************************/
	/** Title: MakeProgram																					
	/** Description: Builds the program out of string									
	/*************************************************************************************************************/
	
	public Program MakeProgram ()
	{
		String input = "dealsWith(a, b):- imports(a, c), exports(b, c) & 0.8"
				+ " \n dealsWith(a, b):- dealsWith(a, f), dealsWith(f, b) & 0.5 "
				+ "\n dealsWith(a, b):- dealsWith(b, a) & 1";
		ParseProgram pp = new ParseProgram(input);
		return pp.getProgram();
	}
}
